package com.simpou.commons.utils.cipher;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;


/**
 * Trata das operacoes sobre hash em arquivos e textos.
 *
 * @author Jonas Pereira
 * @since 2011-07-12
 * @version 2012-05-13
 */
public class Hashs {
    /**
     * MessageDigest.
     */
    private static final MessageDigest MD = MessageDigests.getDigest(
            "SHA-256");

    /**
     * Codificação de caracteres utilizada.
     */
    private static final String DEFAULT_CHARSET_NAME = "UTF8";

    /**
     * <p>getHash.</p>
     *
     * @param file Arquivo.
     * @return Hash SHA-256 de um arquivo.
     * @throws java.io.IOException Erro ao acessar arquivo.
     */
    public static synchronized String getHash(final File file)
        throws IOException {
        byte[] dataBytes = new byte[1024];

        int nread = 0;

        MD.reset();

        FileInputStream fis = new FileInputStream(file);

        try {
            while ((nread = fis.read(dataBytes)) != -1) {
                MD.update(dataBytes, 0, nread);
            }
        } finally {
            fis.close();
        }

        byte[] mdbytes = MD.digest();

        //convert the byte to hex format method 1
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < mdbytes.length; i++) {
            sb.append(Integer.toString((mdbytes[i] & 0xff) + 0x100, 16)
                             .substring(1));
        }

        return sb.toString();
    }

    /**
     * Gera o hash 256 de uma string considerando que não serão gerados erros
     * de encoding e de algoritmo não suportado (que é o que normalmente acontece).
     * Em caso de erro uma assertiva irá sinalizar que isto não pode ocorrer,
     * cabendo ao código cliente se adaptar, usando outro método de hash que lança
     * exceções para esses casos.
     *
     * @param string Texto.
     * @return Hash SHA-256 de um texto.
     */
    public static synchronized String getHashNoErrors(final String string) {
        try {
            return getHash(string);
        } catch (Exception ex) {
            ex.printStackTrace();
            assert false;

            return null;
        }
    }

    /**
     * <p>getHash.</p>
     *
     * @param string Texto.
     * @return Hash SHA-256 de um texto.
     * @throws java.security.NoSuchAlgorithmException Erro ao processar SHA-256.
     * @throws java.io.UnsupportedEncodingException Codificação UTF-8 não suportada.
     */
    public static synchronized String getHash(final String string)
        throws NoSuchAlgorithmException, UnsupportedEncodingException {
        MD.reset();
        MD.update(string.getBytes(DEFAULT_CHARSET_NAME));

        byte[] byteData = MD.digest();

        //convert the byte to hex format method 1
        StringBuilder sb = new StringBuilder();

        for (int i = 0; i < byteData.length; i++) {
            sb.append(Integer.toString((byteData[i] & 0xff) + 0x100, 16)
                             .substring(1));
        }

        return sb.toString();
    }
}
